#include <Eigen/Dense>
#include <pybind11/embed.h> // everything needed for embedding
#include <iostream>

#include<pybind11/eigen.h>

namespace py = pybind11;


#include <opencv2/opencv.hpp>

using cv::Mat;

typedef Eigen::Matrix<unsigned int, Eigen::Dynamic, Eigen::Dynamic> MatrixInt;

#include <stdio.h>
#include <sys/timeb.h>


#define FIX_DEPTH 3
#define FIX_NET_SIZE 416

class Detector {
public:
    Detector(py::module &py_interface_, const int height, const int width) {
        m_py_interface = py_interface_;
        m_height = height;
        m_width = width;
        m_depth = FIX_DEPTH;
        init(m_height, m_width);
    }

    void init(int height, int width) {

        m_py_interface.attr("init")();

    }

    MatrixInt detect(cv::Mat img, bool verbose = false) {
        if (verbose) {
            struct timeb t1;
            ftime(&t1);
            std::cout << "time:" << t1.time << "." << t1.millitm << std::endl;
        }
        int shape[3] = {m_height, m_width, m_depth};
        py::array_t<unsigned char> img_arr(shape, img.data);
        if (verbose) {
            struct timeb t1;
            ftime(&t1);
            std::cout << "time:" << t1.time << "." << t1.millitm << std::endl;
        }
        py::object result = m_py_interface.attr("probe_mat")(img_arr);
        MatrixInt res = result.cast<MatrixInt>();
        if (verbose) {
            this->visualise(res, img);
        }
        return res;
    }

    void visualise(MatrixInt matrix, cv::Mat img, bool show = true, int elapse_time = 40) {
        int r = matrix.rows();

        for (int i = 0; i < r; ++i) {
            int xmin = matrix(i, 0);
            int ymin = matrix(i, 1);
            int xmax = matrix(i, 2);
            int ymax = matrix(i, 3);
            cv::rectangle(img, cvPoint(xmin, ymin), cvPoint(xmax, ymax), cv::Scalar(0, 0, 255), 1, 1, 0);
        }
        if (show) {
//            cv::namedWindow("image", 0);
//            cv::resizeWindow("image", 960, 540);
            imshow("image", img);
            cvWaitKey(elapse_time);
        }

    }

private:
    py::module m_py_interface;
    int m_height;
    int m_width;
    int m_depth;
};


int main() {


    cv::VideoCapture cap("./DJI_0009_360p.avi");
    cv::Mat img;
    cap >> img;
    if (img.empty()) {
        return -1;
    }
    int height = img.rows;
    int width = img.cols;
    float rate = float(height) / float(width);
    cv::resize(img, img, cv::Size(FIX_NET_SIZE, int(FIX_NET_SIZE * rate)));
    height = int(FIX_NET_SIZE * rate);
    width = FIX_NET_SIZE;
//    imshow("video", img);
//    cv::waitKey(0);
    py::scoped_interpreter guard{};
    py::module py_interface = py::module::import("net_interface");
    Detector det = Detector(py_interface, height, width);
    MatrixInt ret = det.detect(img, false);

    while (1) {
        cv::Mat img;
        cap >> img;
        if (img.empty()) {
            break;
        }
        cv::resize(img, img, cv::Size(FIX_NET_SIZE, int(FIX_NET_SIZE * rate)));
//        imshow("video", img);
//        cv::waitKey(1);
        try {

            struct timeb t1;
            ftime(&t1);
            MatrixInt ret = det.detect(img, false);
            struct timeb t2;
            ftime(&t2);
            std::cout << "full cost time:" << double(t2.time - t1.time) + double(t2.millitm - t1.millitm) / 1000
                      << std::endl;
            det.visualise(ret, img, true, 40);

        }
        catch (std::exception ex) {
            std::cout << "ERROR   : " << ex.what() << std::endl;
        }
    }
    return 0;
}
